/**
 * @file feature_engine.h
 * @brief 特征引擎类的定义
 * 
 * @details 该文件定义了特征引擎类，用于计算交易策略所需的各种特征。
 * 当前实现了两个特征：市场价格和主动交易数量比率。
 * 这些特征可以用于分析市场状况并进行交易决策。
 * 
 * @author 原作者
 * @date 2023
 */

#pragma once

#include "common/macros.h"
#include "common/logging.h"

using namespace Common;

namespace Trading {
  /**
   * @brief 特征无效值的哨兵值
   * 
   * @details 该常量用于表示无效或未初始化的特征值。
   * 使用静默NaN（Not a Number）作为特征的无效值标记，
   * 便于在计算中识别和处理特殊情况。
   */
  constexpr auto Feature_INVALID = std::numeric_limits<double>::quiet_NaN();

  /**
   * @brief 特征引擎类
   * 
   * @details 该类负责计算和管理交易策略所需的各种特征。
   * 当前实现了两个特征：
   * 1. 市场价格（Market Price）：基于买卖盘口计算的加权平均价格
   * 2. 主动交易数量比率（Aggressive Trade Quantity Ratio）：交易数量与相应盘口数量的比率
   * 
   * 这些特征可以用于分析市场状况、预测价格走势并进行交易决策。
   */
  class FeatureEngine {
  public:
    /**
     * @brief 构造函数
     * 
     * @details 初始化特征引擎对象，设置日志记录器。
     * 特征值初始化为无效值（Feature_INVALID）。
     * 
     * @param logger 日志记录器指针，用于记录特征计算过程和结果
     */
    FeatureEngine(Common::Logger *logger)
        : logger_(logger) {
    }

    /**
     * @brief 处理订单簿变化并计算公平市场价格
     * 
     * @details 当订单簿发生变化时，该方法会计算公平市场价格特征。
     * 公平市场价格基于买卖盘口的加权平均计算，权重为对应的数量。
     * 计算公式：市场价格 = (买一价 * 卖一量 + 卖一价 * 买一量) / (买一量 + 卖一量)
     * 
     * @param ticker_id 证券标识符
     * @param price 变化的价格
     * @param side 变化的买卖方向
     * @param book 市场订单簿指针，包含当前的盘口状态
     * 
     * @note 该方法不会抛出异常，使用 noexcept 修饰符来提高性能
     */
    auto onOrderBookUpdate(TickerId ticker_id, Price price, Side side, MarketOrderBook* book) noexcept -> void {
      const auto bbo = book->getBBO();
      if(LIKELY(bbo->bid_price_ != Price_INVALID && bbo->ask_price_ != Price_INVALID)) {
        mkt_price_ = (bbo->bid_price_ * bbo->ask_qty_ + bbo->ask_price_ * bbo->bid_qty_) / static_cast<double>(bbo->bid_qty_ + bbo->ask_qty_);
      }

      logger_->log("%:% %() % ticker:% price:% side:% mkt-price:% agg-trade-ratio:%\n", __FILE__, __LINE__, __FUNCTION__,
                   Common::getCurrentTimeStr(&time_str_), ticker_id, Common::priceToString(price).c_str(),
                   Common::sideToString(side).c_str(), mkt_price_, agg_trade_qty_ratio_);
    }

    /**
     * @brief 处理交易事件并计算主动交易数量比率
     * 
     * @details 当发生交易时，该方法会计算主动交易数量比率特征。
     * 该特征表示交易数量与相应盘口数量的比率，可用于衡量交易的侧重性和市场冲击。
     * 计算公式：
     * - 如果是买入交易：交易数量 / 卖一量
     * - 如果是卖出交易：交易数量 / 买一量
     * 
     * @param market_update 市场更新指针，包含交易信息
     * @param book 市场订单簿指针，包含当前的盘口状态
     * 
     * @note 该方法不会抛出异常，使用 noexcept 修饰符来提高性能
     */
    auto onTradeUpdate(const Exchange::MEMarketUpdate *market_update, MarketOrderBook* book) noexcept -> void {
      const auto bbo = book->getBBO();
      if(LIKELY(bbo->bid_price_ != Price_INVALID && bbo->ask_price_ != Price_INVALID)) {
        agg_trade_qty_ratio_ = static_cast<double>(market_update->qty_) / (market_update->side_ == Side::BUY ? bbo->ask_qty_ : bbo->bid_qty_);
      }

      logger_->log("%:% %() % % mkt-price:% agg-trade-ratio:%\n", __FILE__, __LINE__, __FUNCTION__,
                   Common::getCurrentTimeStr(&time_str_),
                   market_update->toString().c_str(), mkt_price_, agg_trade_qty_ratio_);
    }

    /**
     * @brief 获取市场价格特征
     * 
     * @details 返回当前计算的市场价格特征值。
     * 如果特征尚未计算，则返回 Feature_INVALID。
     * 
     * @return double 市场价格特征值
     * 
     * @note 该方法不会抛出异常，使用 noexcept 修饰符来提高性能
     */
    auto getMktPrice() const noexcept {
      return mkt_price_;
    }

    /**
     * @brief 获取主动交易数量比率特征
     * 
     * @details 返回当前计算的主动交易数量比率特征值。
     * 如果特征尚未计算，则返回 Feature_INVALID。
     * 
     * @return double 主动交易数量比率特征值
     * 
     * @note 该方法不会抛出异常，使用 noexcept 修饰符来提高性能
     */
    auto getAggTradeQtyRatio() const noexcept {
      return agg_trade_qty_ratio_;
    }

    /**
     * @brief 已删除的构造函数和赋值运算符
     * 
     * @details 为了防止意外复制或移动特征引擎对象，禁用了默认构造函数、复制构造函数、
     * 移动构造函数以及复制赋值和移动赋值运算符。
     * 特征引擎对象应该始终使用显式的日志记录器初始化。
     */
    FeatureEngine() = delete;

    FeatureEngine(const FeatureEngine &) = delete;

    FeatureEngine(const FeatureEngine &&) = delete;

    FeatureEngine &operator=(const FeatureEngine &) = delete;

    FeatureEngine &operator=(const FeatureEngine &&) = delete;

  private:
    /**
     * @brief 时间字符串缓存
     * 
     * @details 用于日志记录的时间字符串缓存，避免重复创建时间字符串
     */
    std::string time_str_;
    
    /**
     * @brief 日志记录器指针
     * 
     * @details 用于记录特征计算过程和结果的日志记录器指针
     */
    Common::Logger *logger_ = nullptr;

    /**
     * @brief 特征值存储
     * 
     * @details 特征引擎计算的两个特征值：
     * - mkt_price_: 市场价格特征，基于买卖盘口的加权平均计算
     * - agg_trade_qty_ratio_: 主动交易数量比率特征，表示交易数量与相应盘口数量的比率
     * 
     * 初始化为 Feature_INVALID，表示特征尚未计算
     */
    double mkt_price_ = Feature_INVALID, agg_trade_qty_ratio_ = Feature_INVALID;
  };
}
